#include <vga_text.h>
#include <string.h>
#include <stdarg.h>

char _hexBase[16] = {'0','1','2','3','4','5',
					'6','7','8','9','a',
					'b','c','d','e','f'};

void vga_text_mode_set_color(uint8_t color)
{
	_color = color;
}

void vga_text_mode_update_location()
{
	_curLocation = _curY * COLS + _curX;
}

void vga_text_mode_putc(char c)
{
	uint16_t attribute = _color << 8;
	if(c == 0)
		return;
	if(c == '\b' && _curX)
	{
		_curX--;
		char blank = ' ';
		uint16_t *location = (uint16_t *)VGA_COLOR_TEXT_MODE_ADDRESS + (_curY * COLS + _curX);
		*location = blank | attribute;
	}
	else if(c == '\t')
		_curX = (_curX+8) & ~(8-1);
	else if(c == '\r')
		_curX = 0;
	else if(c == '\n')
	{
		_curX = 0;
		_curY++;
	}
	else if(c >= ' ')
	{
		uint16_t *location = (uint16_t *)VGA_COLOR_TEXT_MODE_ADDRESS + (_curY * COLS + _curX);
		*location = c | attribute;
		_curX++;
	}

	if(_curX >= COLS)
	{
		_curX = 0;
		_curY++;
	}

	if(_curY >= LINES)
		vga_text_mode_scroll_line();
	
	vga_text_mode_update_location();
}

void vga_text_mode_puts(char *str)
{
	if(!str)
		return;
	size_t i;
	for(i = 0;i < strlen(str);i++)
		vga_text_mode_putc(str[i]);
}

void vga_text_mode_cls()
{
	uint16_t *p = (uint16_t *)VGA_COLOR_TEXT_MODE_ADDRESS;
	
	uint16_t attribute = _color << 8;

	for(int i = 0;i < LINES * COLS;i++)
	{
		p[i] = attribute | ' ';
	}
	_curX = 0;
	_curY = 0;
	vga_text_mode_goto(0,0);
}

void vga_text_mode_goto(uint8_t x,uint8_t y)
{
	if(x < COLS)
		_curX = x;
	if(y < LINES)
		_curY = y;

	vga_text_mode_update_location();
}

void vga_text_mode_scroll_line()
{
	unsigned char *p = (unsigned char*) VGA_COLOR_TEXT_MODE_ADDRESS;

	int i;
	for (i=0;i<LINES*COLS*2-160;i++) {
		p[i] = p[i+160];
	}

	for (i=LINES*COLS*2-160;i<LINES*COLS*2;i+=2) {
		p[i] = ' ';
		p[i+1] = _color;
	}

#if 0
	// i do not know why this code can no execute
	uint16_t *location = (uint16_t *)VGA_COLOR_TEXT_MODE_ADDRESS;

	int i;
	for(i = 0;i < 24 * 80;i++)
	{
		location[i] = location[i + 80];
	}

	uint16_t attribute = _color << 8;

	for(i = 24 * COLS;i < 25 * 80;i++)
		location[i] = attribute | ' ';

#endif
	_curY = 24;
}

uint16_t vga_text_mode_get_cur_location()
{
	return _curLocation;
}
uint8_t vga_text_mode_get_cursorX()
{
	return _curX;
}
uint8_t vga_text_mode_get_cursorY()
{
	return _curY;
}

void itoa(int i,char *buf)
{
	int count = 0;
	int begin,end;
	if(i < 0)
	{
		buf[0] = '-';
		count = 1;
		begin = 1;
		i *= -1;
	}
	else
	{
		begin = 0;
		count = 0;
	}

	if(i == 0)
	{
		buf[0] = '0';
		buf[1] = '\n';
		return;
	}

	while(i > 0)
	{
		buf[count] = i % 10 + '0';
		i /= 10;
		count++;
	}
	buf[count] = '\0';


	end = count - 1;

	while(begin < end)
	{
		char temp = buf[begin];
		buf[begin] = buf[end];
		buf[end] = temp;
		begin++;
		end--;
	}
}

void itoa_x(int i,char *buf)
{
	int count = 0;
	int begin,end;
	if(i < 0)
	{
		buf[0] = '-';
		count = 1;
		begin = 1;
		i *= -1;
	}
	else
	{
		begin = 0;
		count = 0;
	}

	if(i == 0)
	{
		buf[0] = '0';
		buf[1] = '\0';
		return;
	}

	while(i > 0)
	{
		int temp = i % 16;
		buf[count] = _hexBase[temp];
		i /= 16;
		count++;
	}

	buf[count] = '\0';

	end = count - 1;

	while(begin < end)
	{
		char temp = buf[begin];
		buf[begin] = buf[end];
		buf[end] = temp;
		begin++;
		end--;
	}
}

void itoa_b(unsigned int i,char *buf)
{
	int count = 0;
	int begin,end;

	begin = 0;
	count = 0;

	if(i == 0)
	{
		buf[0] = '0';
		buf[1] = '\0';

		return;
	}

	while(i > 0)
	{
		int temp = i % 2;
		buf[count] = _hexBase[temp];
		i /= 2;
		count++;
	}

	//while(count < 32)
	//{
	//	buf[count] = '0';
	//	count++;
	//}

	buf[count] = '\0';

	end = count - 1;

	while(begin < end)
	{
		char temp = buf[begin];
		buf[begin] = buf[end];
		buf[end] = temp;
		begin++;
		end--;
	}
}

int vga_text_mode_printk(const char *fmt,...)
{
	va_list args;

	va_start(args,fmt);

	size_t i = 0;
	for(i = 0;i < strlen(fmt);i++)
	{
		switch(fmt[i])
		{
		case '%':
			{
				switch(fmt[i + 1])
				{
				case 'c':
					{
						char c = va_arg(args,char);
						vga_text_mode_putc(c);
						i++;
						break;
					}
				case 'd':
					{
						int c = va_arg(args,int);
						char buf[32];
						itoa(c,buf);
						vga_text_mode_puts(buf);
						i++;
						break;
					}
				case 's':
					{
						int c = (int&)va_arg(args,char);
						char buf[64];
						strcpy(buf,(const char *)c);
						vga_text_mode_puts(buf);
						i++;
						break;
					}
				case 'x':
					{
						int c = va_arg(args,int);
						char buf[32];
						itoa_x(c,buf);
						vga_text_mode_puts(buf);
						i++;
						break;
					}
				case 'b':
					{
						unsigned int c = (unsigned int&)va_arg(args,unsigned int);
						char buf[64];
						itoa_b(c,buf);

						vga_text_mode_puts(buf);
						i++;
						break;
					}
				default:
					va_end(args);
					break;
				}
				break;
			}
		default:
			vga_text_mode_putc(fmt[i]);
			break;
		}
	}

	va_end(args);
	return i;
}